﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;


//http://www.interact-sw.co.uk/iangblog/2004/09/16/permuterate
// Date: 07th August 2010
namespace pardusshipcalc.PermutationEngine
{
    class Permutation
    {
        #region OTHERSCODE
        // Returns an enumeration of enumerators, one for each permutation of the input.
        public static IEnumerable<IEnumerable<T>> Permute<T>(IEnumerable<T> list, int count)
        {
            if (count == 0)
            {
                yield return new T[0];
            }
            else
            {
                int startingElementIndex = 0;
                foreach (T startingElement in list)
                {
                    IEnumerable<T> remainingItems = AllExcept(list, startingElementIndex);

                    foreach (IEnumerable<T> permutationOfRemainder in Permute(remainingItems, count - 1))
                    {
                        yield return Concat<T>(
                            new T[] { startingElement },
                            permutationOfRemainder);
                    }
                    startingElementIndex += 1;
                }
            }
        }

        // Enumerates over contents of both lists.
        public static IEnumerable<T> Concat<T>(IEnumerable<T> a, IEnumerable<T> b)
        {
            foreach (T item in a) { yield return item; }
            foreach (T item in b) { yield return item; }
        }

        // Enumerates over all items in the input, skipping over the item
        // with the specified offset.
        public static IEnumerable<T> AllExcept<T>(IEnumerable<T> input, int indexToSkip)
        {
            int index = 0;
            foreach (T item in input)
            {
                if (index != indexToSkip) yield return item;
                index += 1;
            }
        }

        // OTHERS CODE
        #endregion


        public void writePermutationFileToDisk<T>(string tameDir, IEnumerable<T> intInput, int count)
        {
            TextWriter FileWriter = null;
            ulong counter = 0;
            //ulong factorial = (ulong)CalculateFactorial(count);
            bool error = false;
            try
            {
                FileWriter = new StreamWriter(tameDir + "permutations.txt");
            }
            catch
            {
                error = true;
                MessageBox.Show("Failed to open Streamwriter");
            }
            if (error != true)
            {
                foreach (IEnumerable<T> permutation in Permutation.Permute<T>(intInput, count))
                {
                    foreach (T i in permutation)
                    {
                        //FileWriter.Write(" " + i);
                    }
                    //FileWriter.WriteLine();
                    counter++;
                }
                //FileWriter.Close();
                MessageBox.Show("Okay! " + (CalculatePossiblePermutations(34,5)) + " Permutations Possible." + Environment.NewLine + "" + counter + " Have been recorded.");
            }
        }

        // Recursively calculates the Factorial of a number.
        public ulong CalculateFactorial(ulong number)
        {
            if (number <= 1)
            {
                return 1;
            }
            if (number > 19)
            {
                MessageBox.Show("YOU MANIAC: The datatype for this factorial cannot store factorials greater than 19. Please, PLEASE don't ever try that again, or your CPU will cry", "Number is too big!", MessageBoxButtons.OK, MessageBoxIcon.Error);
               return 1;
            }
            else
            {
                return number * CalculateFactorial(number - 1);
            }
        }



        // Used to calcualte how many permutations of chromosomes there might be.
        // generally speaking - we'd want the repeated sequences, so as to ensure the algorithm goes through every possible combination.
        // these repeated sequences will be filtered after the algorithm has completed its search
        // for 34 guns, 5 combinations - the number of unrepeated sequences is: 33390720
        //http://www.mathsisfun.com/combinatorics/combinations-permutations-calculator.html
        public ulong CalculatePossiblePermutations(double dataSetSize, double permutationSetSize)
        {
            // 35 guns in pardus, in groups of 5 = 52521,875.
            // divide that number by the amount of possible repeated sequences: 5*4*3*2*1 to filter it out. (5 factorial)
            // Permutation with repetition is (data set size ^ group size per permutation)
            ulong possiblePermutations = (ulong)(Math.Pow(dataSetSize, permutationSetSize));

            /*if (allowRepeatedPermutations == false)
            {
                // Determine factorial of set size.
               // CalculateFactorial(permutationSetSize);

                // Divide down the total permutations to remove repetitions.
                // Factorials far too big to fit into standard data types
                //possiblePermutations = ((Factorial(dataSetSize)) / (Factorial(dataSetSize -  permutationSetSize)));
            }*/
            

            return possiblePermutations;
        }

        // Use -1 as randMax to not use a rand_max value.
        public int GenerateRandomlySeededRandomNumber(int randMax)
        {
            int randomNumber = 0;
            DateTime DateClass = new DateTime();
            // Use the time in miliseconds multiplied by the  Log of the tick count divided by 1000 as the random seed.
            Random RandClass = new Random((int)((DateClass.Millisecond * Math.Log(DateClass.Ticks)) / 1000));

            if (randMax >= 0)
            {
                randomNumber = RandClass.Next(randMax);
            }
            else
            {
                randomNumber = RandClass.Next();
            }

            return randomNumber;
        }
    }
}
